using System;
using System.Collections.Generic;
using System.Text;
using Xyne.Objects;
using Xyne.Network;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using Microsoft.Xna.Framework.Net;

namespace Xyne.Actions
{
    public enum ActionTypes
    {
        Unknown = 0,
        AlignmentChange,
        DirectHealth,
        RepeatHealth,
        Projectile
    }

    public enum ActionObjects
    {
        Unknown = 0,
        PlayerWizard,
        EnemyWizard
    }

    public enum GameObjects
    {
        Unknown = 0,
        LeftWizard,
        RightWizard
    }   

    /// <summary>
    /// Generic action (i.e. Fireball).
    /// </summary>
    public abstract class Action : NetworkData, IXmlSerializable
    {
        private ActionTypes type = ActionTypes.Unknown;
        public ActionTypes Type
        {
            get { return type; }
            set { type = value; }
        }

        private ActionObjects origin;
        public ActionObjects Origin
        {
            get { return origin; }
            set { origin = value; }
        }

        private GameObjects actualOrigin;
        public GameObjects ActualOrigin
        {
            get { return actualOrigin; }
            set { actualOrigin = value; }
        }

        private List<ActionObjects> targets = new List<ActionObjects>();
        public List<ActionObjects> Targets
        {
            get { return targets; }
        }

        private List<GameObjects> actualTargets = new List<GameObjects>();
        public List<GameObjects> ActualTargets
        {
            get { return actualTargets; }
        }

        private int level;
        public int Level
        {
            get { return level; }
            set { level = value; }
        }

        private int minLevel;
        public int MinLevel
        {
            get { return minLevel; }
            set { minLevel = value; }
        }

        private int maxLevel;
        public int MaxLevel
        {
            get
            {
                return maxLevel;
            }

            set
            {
                maxLevel = value;
            }
        }

        private TriggeredAction onCollision;
        public TriggeredAction OnCollision
        {
            get { return onCollision; }
            set { onCollision = value; }
        }

        public Action()
        {
            DataType = NetworkDataType.Action;
        }

        public Action(Action action)
        {
            Type = action.Type;
            Origin = action.Origin;
            ActualOrigin = action.ActualOrigin;
            targets = new List<ActionObjects>(action.Targets);
            //actualTargets = new List<ActionObjects>(action.ActualTargets);
            Level = action.Level;
            MinLevel = action.minLevel;
            MaxLevel = action.maxLevel;
        }

        public static Action GetCopy(Action action)
        {
            Action ret = null;

            switch (action.Type)
            {
                case ActionTypes.AlignmentChange: ret = new AlignmentChangeAction(action); break;
                case ActionTypes.DirectHealth: ret = new DirectHealthAction(action); break;
                case ActionTypes.RepeatHealth: ret = new RepeatHealthAction(action); break;
                case ActionTypes.Projectile: ret = new ProjectileAction(action); break;
            }

            if (ret != null && action.OnCollision != null)
            {
                TriggeredAction onCollision = new TriggeredAction();
                onCollision.MinValue = action.OnCollision.MinValue;
                onCollision.TriggeredValue = action.OnCollision.TriggeredValue;
                onCollision.Action = Action.GetCopy(action.OnCollision.Action);
                ret.OnCollision = onCollision;
            }

            return ret;
        }

        public void InitializeActualObjects()
        {
            if (origin == ActionObjects.PlayerWizard)
            {
                if (Game.Network.Wizard == GameObjects.LeftWizard)
                {
                    actualOrigin = GameObjects.LeftWizard;
                }
                else if (Game.Network.Wizard == GameObjects.RightWizard)
                {
                    actualOrigin = GameObjects.RightWizard;
                }
            }
            else if (origin == ActionObjects.EnemyWizard)
            {
                if (Game.Network.Wizard == GameObjects.LeftWizard)
                {
                    actualOrigin = GameObjects.RightWizard;
                }
                else if (Game.Network.Wizard == GameObjects.RightWizard)
                {
                    actualOrigin = GameObjects.LeftWizard;
                }
            }

            foreach (ActionObjects actionObject in targets)
            {
                if (actionObject == ActionObjects.PlayerWizard)
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                    {
                        actualTargets.Add(GameObjects.LeftWizard);
                    }
                    else if (Game.Network.Wizard == GameObjects.RightWizard)
                    {
                        actualTargets.Add(GameObjects.RightWizard);
                    }
                }
                else if (actionObject == ActionObjects.EnemyWizard)
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                    {
                        actualTargets.Add(GameObjects.RightWizard);
                    }
                    else if (Game.Network.Wizard == GameObjects.RightWizard)
                    {
                        actualTargets.Add(GameObjects.LeftWizard);
                    }
                }
            }
        }

        public void DeserializeAction(PacketReader reader)
        {
            Level = reader.ReadInt32();
            MinLevel = reader.ReadInt32();
            MaxLevel = reader.ReadInt32();
            actualOrigin = (GameObjects)reader.ReadUInt64();

            int numTargets = reader.ReadInt32();
            for (int i = 0; i < numTargets; i++)
            {
                actualTargets.Add((GameObjects)reader.ReadUInt64());
            }

            Boolean hasCollisionAction = reader.ReadBoolean();
            if (hasCollisionAction)
            {
                TriggeredAction action = new TriggeredAction();

                action.TriggeredValue = reader.ReadInt32();

                Action data = Action.ReadActionFromNetwork(reader);
                if (data != null)
                    action.Action = data;

                OnCollision = action;
            }
        }

        public void SerializeAction(PacketWriter writer)
        {
            writer.Write((ulong)type);
            writer.Write(Level);
            writer.Write(MinLevel);
            writer.Write(MaxLevel);
            writer.Write((ulong)actualOrigin);
            
            writer.Write(targets.Count);
            foreach (GameObjects target in actualTargets)
            {
                writer.Write((ulong)target);
            }

            writer.Write(OnCollision != null);
            if (OnCollision != null)
            {
                writer.Write(OnCollision.MinValue);
                writer.Write(OnCollision.TriggeredValue);
                Action.WriteActionToNetwork(writer, OnCollision.Action);
            }
        }

        public virtual void WriteXml(XmlWriter writer)
        {
            writer.WriteAttributeString("Type", type.ToString());
            writer.WriteElementString("Origin", origin.ToString());
            writer.WriteElementString("MinLevel", minLevel.ToString());
            writer.WriteElementString("MaxLevel", maxLevel.ToString());
            writer.WriteStartElement("Targets");
            foreach (ActionObjects obj in targets)
            {
                writer.WriteElementString("Object", obj.ToString());
            }
            writer.WriteEndElement();
            if (OnCollision != null)
            {
                writer.WriteStartElement("OnCollision");
                writer.WriteAttributeString("MinValue", OnCollision.MinValue.ToString());
                writer.WriteAttributeString("TriggeredValue", OnCollision.TriggeredValue.ToString());
                writer.WriteStartElement("Action");
                OnCollision.Action.WriteXml(writer);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }

        public void ReadXml(XmlReader reader)
        {
            String myName = reader.LocalName;
            minLevel = 0;
            maxLevel = 4;
            while (reader.Read() && reader.LocalName != myName)
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    if (reader.LocalName == "Origin")
                    {
                        origin = (ActionObjects)Enum.Parse(typeof(ActionObjects), reader.ReadString(),true);
                    }
                    else if (reader.LocalName == "Targets")
                    {
                        while (reader.Read() && reader.LocalName != "Targets")
                        {
                            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "Object")
                            {
                                targets.Add((ActionObjects)Enum.Parse(typeof(ActionObjects), reader.ReadString(), true));
                            }
                        }
                    }
                    else if (reader.LocalName == "OnCollision")
                    {
                        TriggeredAction action = new TriggeredAction();
                        action.MinValue = int.Parse(reader.GetAttribute("MinValue"));
                        action.TriggeredValue = int.Parse(reader.GetAttribute("TriggeredValue"));

                        while (reader.Read() && reader.LocalName != "OnCollision")
                        {
                            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "Action")
                            {
                                Action data = Action.ReadActionFromXml(reader);
                                if (data != null)
                                    action.Action = data;
                            }
                        }

                        OnCollision = action;
                    }
                    else if (reader.LocalName == "MinLevel")
                    {
                        try
                        {
                            minLevel = int.Parse(reader.ReadString());
                        } catch(Exception ex)
                        {
                        }
                    }
                    else if (reader.LocalName == "MaxLevel")
                    {
                        try
                        {
                            maxLevel = int.Parse(reader.ReadString());
                        }
                        catch (Exception ex)
                        {
                        }
                    }

                    else ReadNode(reader); // might be derived class
                }
            }
        }

        public abstract void ReadNode(XmlReader reader);

        public XmlSchema GetSchema()
        {
            return null;
        }

        public static Action ReadActionFromXml(XmlReader reader)
        {
            ActionTypes type = (ActionTypes)Enum.Parse(typeof(ActionTypes), reader.GetAttribute("Type"), true);
            switch (type)
            {
                case ActionTypes.AlignmentChange:
                    AlignmentChangeAction alignment = new AlignmentChangeAction();
                    alignment.ReadXml(reader);
                    return alignment;
                case ActionTypes.DirectHealth:
                    DirectHealthAction direct = new DirectHealthAction();
                    direct.ReadXml(reader);
                    return direct;
                case ActionTypes.RepeatHealth:
                    RepeatHealthAction repeat = new RepeatHealthAction();
                    repeat.ReadXml(reader);
                    return repeat;
                case ActionTypes.Projectile:
                    ProjectileAction projectile = new ProjectileAction();
                    projectile.ReadXml(reader);
                    return projectile;
                default:
                    return null;
            };
        }

        public static void WriteActionToNetwork(PacketWriter writer, Action action)
        {
            switch (action.Type)
            {
                case ActionTypes.AlignmentChange:
                    ((AlignmentChangeAction)action).Serialize(writer);
                    break;
                case ActionTypes.DirectHealth:
                    ((DirectHealthAction)action).Serialize(writer);
                    break;
                case ActionTypes.RepeatHealth:
                    ((RepeatHealthAction)action).Serialize(writer);
                    break;
                case ActionTypes.Projectile:
                    ((ProjectileAction)action).Serialize(writer);
                    break;
            }
        }

        public static Action ReadActionFromNetwork(PacketReader reader)
        {
            switch ((ActionTypes)reader.ReadUInt64())
            {
                case ActionTypes.AlignmentChange:
                    AlignmentChangeAction alignment = AlignmentChangeAction.Deserialize(reader);
                    return alignment;
                case ActionTypes.DirectHealth:
                    DirectHealthAction direct = DirectHealthAction.Deserialize(reader);
                    return direct;
                case ActionTypes.RepeatHealth:
                    RepeatHealthAction repeatHealth = RepeatHealthAction.Deserialize(reader);
                    return repeatHealth;
                case ActionTypes.Projectile:
                    ProjectileAction thrownHealth = ProjectileAction.Deserialize(reader);
                    return thrownHealth;
            }

            return null;
        }
    }
}
